package com.nbsaas.boot;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

/**
 * 一种基于Feistel网络的自定义、高性能Long加密实现。
 * * 警告：这是一个用于演示目的的自定义算法（“安全通过晦涩”）。
 * 它没有经过严格的密码学分析。
 * 对于生产环境中的高安全性需求，请始终使用标准、经过审查的算法，如 AES-GCM。
 */
public class ObscureLongHandle implements LongHandle {

    private static final int NUM_ROUNDS = 16;
    private static final int HASH_BYTES = 32; // SHA-256

    // 从密钥派生而来
    private final long initialWhiteningKey; // 用于初始白化
    private final long finalWhiteningKey;   // 用于最终白化
    private final int[] roundKeys;          // 用于Feistel网络的N个轮密钥

    /**
     * 构造函数，需要一个“秘密密钥”来初始化加密器。
     * 这个密钥是实现“提高破解难度”的关键。
     *
     * @param secretKey 用于加密和解密的秘密字符串。
     */
    public ObscureLongHandle(String secretKey) {
        if (secretKey == null || secretKey.isEmpty()) {
            throw new IllegalArgumentException("Secret key cannot be null or empty");
        }

        try {
            // 1. 使用SHA-256从secretKey派生主密钥
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] masterKey = md.digest(secretKey.getBytes(StandardCharsets.UTF_8));
            
            ByteBuffer keyBuffer = ByteBuffer.wrap(masterKey);

            // 2. 分配密钥
            // 前8字节 (64位) 用作初始白化
            this.initialWhiteningKey = keyBuffer.getLong(0);
            // 后8字节 (64位) 用作最终白化
            this.finalWhiteningKey = keyBuffer.getLong(8);

            // 3. 生成轮密钥
            // 使用中间的16字节 (128位) 作为轮密钥生成器的种子
            long rkSeed1 = keyBuffer.getLong(16);
            long rkSeed2 = keyBuffer.getLong(24);
            
            this.roundKeys = new int[NUM_ROUNDS];
            for (int i = 0; i < NUM_ROUNDS; i++) {
                // 通过旋转和异或来生成不同的轮密钥
                rkSeed1 = (rkSeed1 << 13) | (rkSeed1 >>> 51); // 旋转
                rkSeed2 = (rkSeed2 >>> 7) | (rkSeed2 << 57);  // 旋转
                // 将两个64位种子混合成一个32位轮密钥
                this.roundKeys[i] = (int)(rkSeed1 ^ rkSeed2) + i; // 加上i增加每轮的差异
            }

        } catch (NoSuchAlgorithmException e) {
            // SHA-256 应该是所有Java环境都支持的
            throw new RuntimeException("Critical Error: SHA-256 algorithm not found", e);
        }
    }

    /**
     * 自定义轮函数 (F-function)
     * 这是算法的核心，需要高性能和非线性。
     * * @param r 32位的右半部分
     * @param roundKey 32位的轮密钥
     * @return
     */
    private int roundFunction(int r, int roundKey) {
        // 1. 旋转 - 制造位移
        int s1 = (r << 9) | (r >>> 23);
        // 2. 混合 - 使用加法（利用2^32溢出）制造非线性
        int m1 = s1 + roundKey;
        // 3. 异或 - 混合
        int s2 = m1 ^ 0xDEADBEEF; // 混入一个魔术常数
        // 4. 再次旋转
        return (s2 << 17) | (s2 >>> 15);
    }

    @Override
    public String encode(Long num) {
        if (num == null) return null;
        
        // 1. 初始白化
        long data = num ^ this.initialWhiteningKey;

        // 2. 拆分为 L 和 R
        int l = (int) (data >>> 32);
        int r = (int) data;

        // 3. Feistel 网络 (N轮)
        for (int i = 0; i < NUM_ROUNDS; i++) {
            int fResult = roundFunction(r, this.roundKeys[i]);
            int tempL = l;
            
            l = r;
            r = tempL ^ fResult;
        }
        
        // 4. 重新组合 (注意：最后一轮后不交换 L 和 R)
        data = ((long) l << 32) | (r & 0xFFFFFFFFL);

        // 5. 最终白化
        data = data ^ this.finalWhiteningKey;

        // 6. 转换为Base64字符串
        ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
        buffer.putLong(data);
        // 使用 URL-safe 的 Base64 编码，以避免在URL或文件名中出现问题
        return Base64.getUrlEncoder().encodeToString(buffer.array());
    }

    @Override
    public Long decode(String encodedString) {
        if (encodedString == null || encodedString.isEmpty()) {
            return null;
        }

        try {
            // 1. 从Base64解码
            byte[] bytes = Base64.getUrlDecoder().decode(encodedString);
            if (bytes.length != Long.BYTES) {
                 throw new IllegalArgumentException("Invalid encoded string length");
            }
            ByteBuffer buffer = ByteBuffer.wrap(bytes);
            long data = buffer.getLong();

            // 2. 逆向最终白化
            data = data ^ this.finalWhiteningKey;

            // 3. 拆分为 L 和 R
            int l = (int) (data >>> 32);
            int r = (int) data;

            // 4. 逆向Feistel网络 (轮密钥倒序使用)
            for (int i = NUM_ROUNDS - 1; i >= 0; i--) {
                int fResult = roundFunction(l, this.roundKeys[i]);
                int tempR = r;
                
                r = l;
                l = tempR ^ fResult;
            }

            // 5. 重新组合
            data = ((long) l << 32) | (r & 0xFFFFFFFFL);
            
            // 6. 逆向初始白化
            data = data ^ this.initialWhiteningKey;

            return data;

        } catch (Exception e) {
            // Base64解码失败或字节长度不匹配
            System.err.println("Failed to decode string: " + e.getMessage());
            // 在生产环境中，可能需要更健壮的异常处理
            return null; 
        }
    }

    // --- 主方法：用于测试 ---
    public static void main(String[] args) {
        String mySecret = "ThisIsA_VeryStrong-and-long_P@ssword!123";
        LongHandle encoder = new ObscureLongHandle(mySecret);

        Long originalValue = 1L;

        System.out.println("Original: " + originalValue);

        String encoded = encoder.encode(originalValue);
        System.out.println("Encoded:  " + encoded);

        Long decoded = encoder.decode(encoded);
        System.out.println("Decoded:  " + decoded);

        System.out.println("Match:    " + originalValue.equals(decoded));

        System.out.println("\n--- 测试边界值 ---");
        long maxValue = Long.MAX_VALUE;
        System.out.println("Original: " + maxValue);
        String encMax = encoder.encode(maxValue);
        System.out.println("Encoded:  " + encMax);
        System.out.println("Decoded:  " + encoder.decode(encMax));
        
        long minValue = Long.MIN_VALUE;
        System.out.println("Original: " + minValue);
        String encMin = encoder.encode(minValue);
        System.out.println("Encoded:  " + encMin);
        System.out.println("Decoded:  " + encoder.decode(encMin));
        
        long zeroValue = 0L;
        System.out.println("Original: " + zeroValue);
        String encZero = encoder.encode(zeroValue);
        System.out.println("Encoded:  " + encZero);
        System.out.println("Decoded:  " + encoder.decode(encZero));

        System.out.println("\n--- 测试密钥敏感性 ---");
        LongHandle wrongEncoder = new ObscureLongHandle("WrongPassword!");
        try {
            Long wrongDecode = wrongEncoder.decode(encoded);
            System.out.println("Decode with wrong key: " + wrongDecode);
            System.out.println("Wrong key result matches original: " + originalValue.equals(wrongDecode));
        } catch (Exception e) {
            System.out.println("Decode with wrong key failed (as expected): " + e.getMessage());
        }
    }
}